---
import ThemeAuto from "@icons/ThemeAuto.astro";
import ThemeDark from "@icons/ThemeDark.astro";
import ThemeLight from "@icons/ThemeLight.astro";

const themes = [
  { id: "light", Icon: ThemeLight },
  { id: "auto", Icon: ThemeAuto },
  { id: "dark", Icon: ThemeDark },
];
---

<theme-switch class="theme-switch relative">
  {
    themes.map((theme) => (
      <label for={`theme-switch-${theme.id}`}>
        <input
          id={`theme-switch-${theme.id}`}
          type="radio"
          name="theme-switch"
          disabled
          value={theme.id}
        />
        <theme.Icon class="icon" />
      </label>
    ))
  }
  <button class="mobile-toggle md:hidden"></button>
</theme-switch>

<script>
  import { createStore, createEvent, combine } from "effector";

  function watchMedia(media: string) {
    const matcher = window.matchMedia(media);
    const changed = createEvent<MediaQueryListEvent>();

    const $matches = createStore(matcher.matches);
    $matches.on(changed, (_, event) => event.matches);

    matcher.addEventListener("change", changed);

    return { $matches, changed };
  }

  class ThemeSwitch extends HTMLElement {
    changeThemeClicked = createEvent<Theme>();
    toggleThemePressed = createEvent();

    systemDarkTheme = watchMedia("(prefers-color-scheme: dark)");
    $theme = createStore(readTheme());
    $isDark = combine(this.$theme, this.systemDarkTheme.$matches, (theme, systemDark) => {
      if (theme === "auto") return systemDark;
      return theme === "dark";
    });

    connectedCallback() {
      this.setupButtons();
      this.setupToggle();

      this.$theme.on(this.changeThemeClicked, (_, theme) => theme);
      this.$theme.on(this.toggleThemePressed, (theme) => {
        const themeIndex = allowedThemeValues.indexOf(theme);
        const nextTheme = allowedThemeValues[(themeIndex + 1) % allowedThemeValues.length];
        return nextTheme as Theme;
      });

      this.$theme.watch((theme) => saveTheme(theme));

      this.$isDark.watch((dark) => {
        if (dark) document.documentElement.classList.add("theme-dark");
        else document.documentElement.classList.remove("theme-dark");
      });
    }

    setupButtons() {
      const radioButtons = Array.from(this.querySelectorAll("input"));

      radioButtons.forEach((radioButton) => {
        this.$theme.watch((theme) => {
          radioButton.checked = theme === radioButton.value;
        });

        // Handle clicks
        radioButton.addEventListener("change", (event) => {
          const target = event.currentTarget as HTMLInputElement;
          console.log("clicked", target.id);
          if (allowedThemeValues.includes(target.value)) {
            this.changeThemeClicked(target.value as Theme);
          }
        });

        radioButton.disabled = false;
      });
    }

    setupToggle() {
      const toggle = this.querySelector(".mobile-toggle") as HTMLButtonElement;
      toggle.addEventListener("click", () => this.toggleThemePressed());
    }
  }

  type Theme = "dark" | "light" | "auto";
  const allowedThemeValues: string[] = ["dark", "light", "auto"] satisfies Theme[];

  function normalizeTheme(input: string | null, defaultValue: Theme = "auto"): Theme {
    if (input && allowedThemeValues.includes(input)) {
      return input as Theme;
    }
    return defaultValue;
  }

  function readTheme(defaultValue: Theme = "auto"): Theme {
    if (typeof localStorage !== undefined) {
      const storedValue = localStorage.getItem("theme");
      return normalizeTheme(storedValue, defaultValue);
    }
    return defaultValue;
  }

  function saveTheme(theme: Theme) {
    if (typeof localStorage !== undefined) {
      localStorage.setItem("theme", theme);
    }
  }

  customElements.define("theme-switch", ThemeSwitch);
</script>

<style>
  .theme-switch {
    @apply flex min-h-8 min-w-10 items-center rounded-full px-2 py-1 md:gap-1;
    background-color: var(--theme-bg);
  }

  .theme-switch > label:focus-within {
    outline: none;
    color: var(--theme-accent);
    @apply ring-1 ring-current ring-offset-2;
  }

  .theme-switch > label {
    @apply relative flex cursor-pointer items-center justify-center rounded-full opacity-50;
    color: var(--theme-code-inline-text);
  }

  .theme-switch .checked {
    opacity: 1;
    color: var(--theme-accent);
  }

  input[name="theme-switch"] {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    opacity: 0;
    z-index: -1;
  }

  input[type="radio"]:not(:checked) ~ .icon {
    @apply hidden md:block;
  }

  input[type="radio"]:checked ~ .icon {
    opacity: 1;
    color: var(--theme-accent);
  }

  .mobile-toggle {
    background-color: rgba(0, 0, 0, 0);
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
  }
</style>
